﻿using AbpDemo.Filters;
using AbpDemo.Menus.Dto;
using AbpDemo.RoleMenus;
using AbpDemo.Roles;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Uow;

namespace AbpDemo.Menus
{
    [ApiExplorerSettings(GroupName = "v1")]
    public class MenuService : CrudAppExtService<Menu, MenuDto, Guid, CreateUpdateMenuDto>, IMenuService
    {
        private readonly IRepository<Menu, Guid> repositoryMenu;
        private readonly IRepository<Role, Guid> repositoryRole;
        private readonly IRepository<RoleMenu, Guid> repositoryRoleMenu;

        public MenuService(
            IRepository<Menu, Guid> repositoryMenu,
            IRepository<Role, Guid> repositoryRole,
            IRepository<RoleMenu, Guid> repositoryRoleMenu
            ) : base(repositoryMenu)
        {
            this.repositoryMenu = repositoryMenu;
            this.repositoryRole = repositoryRole;
            this.repositoryRoleMenu = repositoryRoleMenu;
        }

        List<Guid> Path = new List<Guid>();
        [CustomerAuthorizationFilter("system:menu:edit")]
        public override async Task<ResultDto<MenuDto>> GetAsync(Guid Id)
        {
            var menus = await repositoryMenu.GetListAsync();
            var list = GetParentPath(menus, Id).Where(m => m != Guid.Empty).Reverse().ToList();
            var dto =  await base.GetAsync(Id);
            dto.Data.CheckedNode = list;
            return dto;
        }

        /// <summary>
        /// 删除菜单
        /// </summary>
        /// <param name="Id"></param>
        /// <returns></returns>
        [CustomerAuthorizationFilter("system:menu:delete")]
        public override async Task<ResultDto<bool>> DeleteAsync(Guid Id)
        {
            var hasChild = await repositoryMenu.AnyAsync(m => m.ParentId == Id);
            if(hasChild )
            {
                return new ResultDto<bool> { Code = ResultState.Failed, Message = "还有下级菜单，不允许删除" };
            }
            return await base.DeleteAsync(Id);
        }

        /// <summary>
        /// 根据ID获取路径
        /// </summary>
        /// <param name="menus"></param>
        /// <param name="id"></param>
        /// <returns></returns>
        private List<Guid> GetParentPath(List<Menu> menus, Guid id)
        {
            var menu = menus.FirstOrDefault(m => m.Id == id);
            if (menu != null)
            {
                Path.Add(menu.ParentId);
                GetParentPath(menus, menu.ParentId);
            }

            return Path;
        }

        /// <summary>
        /// 获取路由信息
        /// </summary>
        /// <returns></returns>
        [UnitOfWork]
        [Authorize]
        public async Task<ResultDto<List<RouteDto>>> GetRoute()
        {
            var roles = (await repositoryRole.GetQueryableAsync())
                .Join(await repositoryRoleMenu.GetQueryableAsync(), a => a.Id, b => b.RoleId,
                (a, b) => new { b.MenuId, a.RoleCode }).ToList();

            var list = (await repositoryMenu.GetListAsync(m=>m.MenuType == Enums.MenuType.Menu || m.MenuType == Enums.MenuType.Directory))
                .Select(menu => {
                    RouteDto routeDto = ObjectMapper.Map<Menu, RouteDto>(menu);
                    routeDto.Roles = roles.Where(m => m.MenuId == menu.Id).Select(m => m.RoleCode).ToList();
                    return routeDto;
                }).ToList();

            return new ResultDto<List<RouteDto>> { Code = ResultState.Success, Data = list };
        }

        /// <summary>
        /// 获取递归菜单数据
        /// </summary>
        /// <returns></returns>
        [HttpGet("/Menu/GetTreeNodes")]
        [CustomerAuthorizationFilter("system:menu:page")]
        public async Task<ResultDto<List<TreeDto>>> GetTreeNodes(QueryDto dto)
        {
            var menus = await repositoryMenu.GetQueryableAsync();
            if(!string.IsNullOrEmpty(dto.MenuName) || dto.MenuType != null) 
            { 
                menus = menus.WhereIf(!string.IsNullOrEmpty(dto.MenuName), m => m.MenuName.Contains(dto.MenuName));
                menus = menus.WhereIf(dto.MenuType != null, m => m.MenuType == dto.MenuType);
                List<TreeDto> list = ObjectMapper.Map<List<Menu>, List<TreeDto>>(menus.ToList());
                return new ResultDto<List<TreeDto>> { Code = ResultState.Success, Data = list };
            }
            return new ResultDto<List<TreeDto>> { Code = ResultState.Success, Data = GetTrees(menus.ToList(), Guid.Empty) };
        }


        private List<TreeDto> GetTrees(List<Menu> menus, Guid ParentId)
        {
            List<TreeDto> tree = new List<TreeDto>();

            var list = menus.Where(m => m.ParentId == ParentId);

            foreach (var item in list)
            {
                var treeDto = ObjectMapper.Map<Menu, TreeDto>(item);
                treeDto.Child = !GetTrees(menus, item.Id).Any() ? null : GetTrees(menus, item.Id);
                tree.Add(treeDto);
            }

            return tree;
        }

        [CustomerAuthorizationFilter("system:menu:create")]
        public override Task<ResultDto<MenuDto>> CreateAsync(CreateUpdateMenuDto createInput)
        {
            return base.CreateAsync(createInput);
        }

        [CustomerAuthorizationFilter("system:menu:edit")]
        public override Task<ResultDto<MenuDto>> UpdateAsync(Guid Id, CreateUpdateMenuDto createInput)
        {
            return base.UpdateAsync(Id, createInput);
        }

        [CustomerAuthorizationFilter("system:menu:edit")]
        [HttpPost("/Menu/SwitchHidden")]
        public async Task<ResultDto<bool>> SwitchHidden(MenuDto dto)
        {
            try
            {
                Menu menu = await repositoryMenu.FirstOrDefaultAsync(m => m.Id == dto.Id);
                menu.IsHidden = !menu.IsHidden;
                await repositoryMenu.UpdateAsync(menu);
                return new ResultDto<bool> { Code = ResultState.Success, Data = true };
            }
            catch (Exception e)
            {
                return new ResultDto<bool> { Code = ResultState.Success, Data = false, Message = e.Message };
            }
        }

        [CustomerAuthorizationFilter("system:menu:edit")]
        [HttpPost("/Menu/SwitchAffix")]
        public async Task<ResultDto<bool>> SwitchAffix(MenuDto dto)
        {
            try
            {
                Menu menu = await repositoryMenu.FirstOrDefaultAsync(m => m.Id == dto.Id);
                menu.IsAffix = !menu.IsAffix;
                await repositoryMenu.UpdateAsync(menu);
                return new ResultDto<bool> { Code = ResultState.Success, Data = true };
            }
            catch (Exception e)
            {
                return new ResultDto<bool> { Code = ResultState.Success, Data = false, Message = e.Message };
            }
        }
    }
}
